/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package deatax.display;

import deatax.GameData;
import deatax.Highlight;
import deatax.Tile;
import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU;

/**
 *
 * @author nrechten
 */
public class BoardRenderer implements GLEventListener {

    public static int LOD_ANITALIASING_NICE = 256;
    public static int LOD_ANITALIASING = 128;
    public static int LOD_PARTICLES = 32;
    public static int LOD_FOG = 16;
    public static int LOD_KING = 4;
    public static int LOD_MODELS = 2;

    public static final float tw = DefaultTileRenderer.getHexWidth();
    public static final float th = DefaultTileRenderer.getHexHeight();
    public static float[] POLYGONX = new float[]{-tw, -tw / 2, tw / 2, tw, tw / 2, -tw / 2, -tw};
    public static float[] POLYGONY = new float[]{0, -th, -th, 0, th, th, 0};
    float tgx = tw * 2 - tw / 2;
    float aspect;
    GameData data;
    Point cursor = new Point();
    Tile hover = null;
    public static int[] OFFX = new int[]{0, -1, 0, 1, -1, 1, 0};
    public static int[] OFFY = new int[]{0, -1, -1, -1, 0, 0, 1};
    GLU glu;
    
    public long lastDisplay = 0;
    public static float tickScale = 0;
    public List<TileRenderer> renderers = new ArrayList<TileRenderer>();
    public TileRenderer default_renderer = new DefaultTileRenderer();

    float avgFPS = 0;

    public BoardRenderer(GameData data) {
        this.data = data;
        renderers.add(new ForestTileRenderer());
        renderers.add(new WaterTileRenderer());
        renderers.add(new FishTileRenderer());
    }

    @Override
    public void init(GLAutoDrawable glad) {

        System.out.println("Initialising board renderer");
        //GL2 gl = glad.getGL().getGL2();
        GL2 gl = glad.getGL().getGL2();
        glu = GLU.createGLU();


//        gl.glEnable(GL.GL_CULL_FACE);
//        gl.glCullFace(GL.GL_BACK);

        if (data.checkLOD(LOD_ANITALIASING)) {
            gl.glEnable(GL.GL_BLEND);
            gl.glEnable(GL.GL_LINE_SMOOTH);
//            gl.glEnable(GL2.GL_POLYGON_SMOOTH);
            gl.glEnable(GL2.GL_POINT_SMOOTH);
            if (data.checkLOD(LOD_ANITALIASING_NICE)) {
                gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST);
                gl.glHint(GL2.GL_POLYGON_SMOOTH_HINT, GL2.GL_NICEST);
                gl.glHint(GL2.GL_POINT_SMOOTH_HINT, GL2.GL_NICEST);
            } else {
                gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_FASTEST);
                gl.glHint(GL2.GL_POLYGON_SMOOTH_HINT, GL2.GL_FASTEST);
                gl.glHint(GL2.GL_POINT_SMOOTH_HINT, GL2.GL_FASTEST);
            }

        }

        if (data.checkLOD(LOD_FOG)) {
            gl.glEnable(GL2.GL_FOG);
            gl.glFogi(GL2.GL_FOG_MODE, GL2.GL_LINEAR);
            gl.glFogfv(GL2.GL_FOG_COLOR, new float[]{0.9f, 0.9f, 0.9f, 1.0f}, 0);
            gl.glFogf(GL2.GL_FOG_DENSITY, 0.35f);
            gl.glHint(GL2.GL_FOG_HINT, GL2.GL_DONT_CARE);
            gl.glFogf(GL2.GL_FOG_START, 100.0f);
            gl.glFogf(GL2.GL_FOG_END, 200.0f);
        }

        gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);
        gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
        gl.glDepthFunc(GL.GL_LEQUAL);
        gl.glEnable(GL2.GL_DEPTH_BUFFER);

        Texture.cacheImages(gl);
        Model.clearIds();
    }

    @Override
    public void dispose(GLAutoDrawable glad) {
    }



    @Override
    public void display(GLAutoDrawable glad) {
        if (data.getMap() != null) {
            long tick = 0;
            if (lastDisplay != 0) {
                tick = System.currentTimeMillis() - lastDisplay;
            }
            lastDisplay = System.currentTimeMillis();
            
            tickScale = tick /1000f;

            float fps = tick>0?1000f/tick:0;
            avgFPS = avgFPS*0.9f+fps*0.1f;
            data.getGui().lblFPS.setText( tick + "ms ("+Math.round(fps)+"/" + Math.round(avgFPS) + "fps) - " + GameData.getDetailLevel());
            if(fps < 15 && avgFPS < 20){
                System.out.println("Decreasing level of detail");
                GameData.decLOD();
            } else if(fps > 59 && avgFPS > 59){
                GameData.incLOD();
            }

            default_renderer.update(tick);
            for (TileRenderer tileRenderer : renderers) {
                tileRenderer.update(tick);
            }

            int width = data.getMap().getWidth();
            int height = data.getMap().getHeight();


            GL2 gl = glad.getGL().getGL2();
            gl.glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

            gl.glEnable(GL.GL_BLEND);
            gl.glEnable(GL2.GL_DEPTH_TEST);

            gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);
            gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);

            gl.glColor3f(1.0f, 1.0f, 1.0f);
            data.getCamera().setProjection(gl);

            doHover();

            for (Light light : data.getLights()) {
                light.setup(gl);
            }

            float px, py;
            float rotation = 0;
            for (int p = 0; p < 4; p++) {
                px = startX();
                py = startY();
                rotation = 0;
                for (int y = 0; y < height; y++) {
                    for (int x = 0; x < width; x++) {
                        rotation = (rotation + 60) % 360;
                        py += (x % 2 == 0 ? -th : th);
                        Tile t = data.getMap().getTile(x, y);
                        if (t != null) {
                            findRenderer(t.getTerrain().getName()).draw(gl, t, px, py, rotation, p);
                        }
                        px += tgx;
                    }
                    px = startX();
                    py += th * 2;
                }
                if(p == 0){
                    gl.glClear(GL2.GL_DEPTH_BUFFER_BIT);
                }
            }
            for (Highlight highlight : data.getMap().getHighlights()) {
                for (int i = 0; i < highlight.tiles.length; i += 2) {
                    float[] xy = mapToGrid(highlight.tiles[i], highlight.tiles[i + 1]);
                    default_renderer.drawHighlight(gl, highlight.colour, xy[0], xy[1]);
                }
            }


            if (data.getMiniboard() != null) {
                data.getMiniboard().draw(gl, glu);
            }
            //gl.glFlush();


            if(data.getSelectedTileRenderer() != null){
               data.getSelectedTileRenderer().draw(gl, glu);
            }
        }

//        glad.swapBuffers();
        //gl.glFlush();
    }

    public TileRenderer findRenderer(String type) {
        for (TileRenderer tileRenderer : renderers) {
            if (tileRenderer.renders(type)) {
                return tileRenderer;
            }
        }
        return default_renderer;
    }

    public void doHover() {
        if (hover != null) {
            float[] hov = mapToGrid(hover.getX(), hover.getY());
            if (cursor.distance(hov[0], hov[1]) > th) {
                hover.onHoverOut();
                hover = null;
                _doHover();
            }
        } else {
            _doHover();
        }

    }

    public void _doHover() {

        int[] over = gridToMap(cursor.x, cursor.y);


        Tile t = data.getMap().getTile(over[0], over[1]);
        if (t != null) {
            t.onHoverIn();
            hover = t;
        }

    }

    public float startX() {
        return -data.getMap().getWidth() / 2 * tgx;
    }

    public float startY() {
        return -data.getMap().getHeight() * th;
    }

    public float[] mapToGrid(int x, int y) {
        return new float[]{startX() + x * tgx, startY() + y * th * 2 + (x % 2 == 0 ? -th : 0), 0};
    }

    public int[] gridToMap(float x, float y) {
        int rx = Math.round((x - startX()) / tgx);
        int ry = Math.round((y - startY() - (rx % 2 == 0 ? -th : th)) / (th * 2));
        float f[] = mapToGrid(rx, ry);

        Point p = new Point();
        p.setLocation(x, y);
        double dis = p.distance(f[0], f[1]);
        int closest = 0;
        for (int i = 1; i < 7; i++) {
            f = mapToGrid(rx + OFFX[i], ry + OFFY[i]);
            double temp = p.distance(f[0], f[1]);
            if (temp < dis) {
                dis = temp;
                closest = i;
            }
        }

        return new int[]{rx + OFFX[closest], ry + OFFY[closest]};
    }

    public static void glColor(GL2 gl, Color c) {
        gl.glColor3f(c.getRed() / 255f, c.getGreen() / 255f, c.getBlue() / 255f);
//        gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE,new float[]{c.getRed() / 255f, c.getGreen() / 255f , c.getBlue() / 255f},0);

    }

    public static void glColorB(GL2 gl, Color c) {
        gl.glColor3f(0.8f + c.getRed() / 2550f * 2, 0.8f + c.getGreen() / 2550f * 2, 0.8f + c.getBlue() / 2550f * 2);
//        gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE,new float[]{0.8f+c.getRed() / 2550f * 2, 0.8f+c.getGreen() / 2550f * 2, 0.8f+c.getBlue() / 2550f * 2},0);
    }

    public static void glColorC(GL2 gl, Color c) {
        gl.glColor3f(0.6f + c.getRed() / 2550f * 3, 0.6f + c.getGreen() / 2550f * 3, 0.6f + c.getBlue() / 2550f * 3);
//        gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE,new float[]{0.8f+c.getRed() / 2550f * 2, 0.8f+c.getGreen() / 2550f * 2, 0.8f+c.getBlue() / 2550f * 2},0);

    }

    public void reshape(GLAutoDrawable glad, int x, int y, int w, int h) {
//        GLU glu = new GLU();
//        GL2 gl = glad.getGL().getGL2();
//
////        gl.glViewport(0, 0, w, h);
////        glu.gluPerspective(10,10, 10, 1);
//        System.out.println("gagnuirng");
//        gl.glMatrixMode(GL2.GL_PROJECTION);
//        gl.glLoadIdentity();
//        glu.gluPerspective(50.0, ((float) w) / h, 3.0, 200.0);

        // Get the OpenGL graphics context
        GL2 gl = glad.getGL().getGL2();

        h = (h == 0) ? 1 : h;  // Prevent divide by zero
        aspect = (float) w / h; // Compute aspect ratio

        // Set view port to cover full screen
        gl.glViewport(0, 0, w, h);

        // Set up the projection matrix - choose perspective view
        gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
        gl.glLoadIdentity();    // reset
        // Angle of view (fovy) is 45 degrees (in the up y-direction). Based on this
        // canvas's aspect ratio. Clipping z-near is 0.1f and z-near is 100.0f.
        glu.gluPerspective(45.0f, aspect, 0.1f, 500.0f); // fovy, aspect, zNear, zFar
//      gl.glRotated
        // Switch to the model-view transform
        gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
        gl.glLoadIdentity();    // reset
    }

    private void drawGrid(GL2 gl) {
        gl.glColor3f(1f, 1f, 1f);

        gl.glBegin(GL.GL_LINE_LOOP);
        gl.glVertex2i(100, 0);
        gl.glVertex2i(-100, 0);
        gl.glEnd();

        gl.glBegin(GL.GL_LINE_LOOP);
        gl.glVertex2i(0, 100);
        gl.glVertex2i(0, -100);
        gl.glEnd();

        gl.glBegin(GL.GL_LINE_LOOP);
        gl.glVertex3i(0, 0, 100);
        gl.glVertex3i(0, 0, -100);
        gl.glEnd();
    }

    public Tile getHover() {
        return hover;
    }
}
